﻿using System.Windows;
using System.Windows.Media;

namespace Black.SurfaceToolkit.Tools
{
    /// <summary>
    /// Fournit des fonctions utilitaires.
    /// </summary>
    public static class Utils
    {
        #region Fonctions statiques publiques

        /// <summary>
        /// Effectue une réduction de la valeur de l'angle spécifié.
        /// </summary>
        /// <param name="p_dAngle">Valeur de l'angle à réduire (en degrés).</param>
        /// <returns><paramref name="p_dAngle" /> réduit.</returns>
        public static double CropAngle(double p_dAngle)
        {
            while (p_dAngle < 0 || p_dAngle > 360)
            {
                if (p_dAngle > 360)
                    p_dAngle -= 360;
                else if (p_dAngle < 0)
                    p_dAngle += 360;
            }

            return (p_dAngle);
        }

        /// <summary>
        /// Obtient une valeur indiquant si un point référencé et un élement spécifiés sont en collisions.
        /// </summary>
        /// <param name="p_oPoint">Point à tester.</param>
        /// <param name="p_oElement">Elément à test.</param>
        /// <param name="p_oReference">Référence de calcul.</param>
        /// <returns><see langword="true" /> si <paramref name="p_oPoint" /> référencé dans <paramref name="p_oReference" /> est dans <paramref name="p_oElement" />, <see langword="false" /> sinon.</returns>
        public static bool Intersect(Point p_oPoint, FrameworkElement p_oElement, FrameworkElement p_oReference)
        {
            // Test existance élément

            if (p_oElement == null)
                return (false);

            // Calcul des coordonnées

            Rect oRect = Utils.GetBounds(p_oElement, p_oReference);

            // Test intersection

            if (p_oPoint.X >= oRect.X && p_oPoint.X <= (oRect.X + oRect.Width) && p_oPoint.Y >= oRect.Y && p_oPoint.Y <= (oRect.Y + oRect.Height))
                return (true);

            // Ne se trouve pas dendans

            return (false);
        }

        /// <summary>
        /// Obtient une valeur indiquant si deux élements référencés spécifiés sont en collisions.
        /// </summary>
        /// <param name="p_oElement1">Premier élément à tester.</param>
        /// <param name="p_oElement2">Second élément à tester.</param>
        /// <param name="p_oReference">Référence de calcul.</param>
        /// <returns><see langword="true" /> si <paramref name="p_oElement1" /> et <paramref name="p_oElement2" /> référencés dans <paramref name="p_oReference" /> ont une intersection non vide, <see langword="false" /> sinon.</returns>
        public static bool Intersect(FrameworkElement p_oElement1, FrameworkElement p_oElement2, FrameworkElement p_oReference)
        {
            // Test existance élément

            if (p_oElement1 == null || p_oElement2 == null)
                return (false);

            // Calcul des coordonnées

            Rect oRect1 = Utils.GetBounds(p_oElement1, p_oReference);
            Rect oRect2 = Utils.GetBounds(p_oElement2, p_oReference);

            // Test intersection

            return (oRect1.IntersectsWith(oRect2));
        }

        /// <summary>
        /// Obtient le rectangle englobant de l'élément spécifié par rapport à la référence spécifiée.
        /// </summary>
        /// <param name="p_oElement">Elément dont on veut le rectangle englobant.</param>
        /// <param name="p_oReference">Référence de calcul.</param>
        /// <returns>Rectangle englobant de <paramref name="p_oElement" /> par rapport à <paramref name="p_oReference" />.</returns>
        public static Rect GetBounds(FrameworkElement p_oElement, FrameworkElement p_oReference)
        {
            GeneralTransform oTransform = p_oElement.TransformToVisual(p_oReference);

            return (oTransform.TransformBounds(new Rect(0, 0, p_oElement.ActualWidth, p_oElement.ActualHeight)));
        }

        /// <summary>
        /// Obtient une valeur indiquant si le point spécifié est dans le rectangle spécifié.
        /// </summary>
        /// <param name="p_oPoint">Point à tester.</param>
        /// <param name="p_oRect">Rectangle à tester.</param>
        /// <returns><see langword="true" /> si <paramref name="p_oPoint" /> est dans <paramref name="p_oRect" />, <see langword="false" /> sinon.</returns>
        public static bool PointInRect(Point p_oPoint, Rect p_oRect)
        {
            return (p_oPoint.X >= p_oRect.X && p_oPoint.X <= (p_oRect.X + p_oRect.Width)
                 && p_oPoint.Y >= p_oRect.Y && p_oPoint.Y <= (p_oRect.Y + p_oRect.Height));
        }

        #endregion
    }
}
